/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: fat file system control
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-08-15     JasonHu           Port from xboot
 */

/*
 * kernel/vfs/fat/fat-control.c
 *
 * Copyright(c) 2007-2022 Jianjun Jiang <8192542@qq.com>
 * Official site: http://xboot.org
 * Mobile phone: +86-18665388956
 * QQ: 8192542
 *
 * Permission is hereby granted, NX_MemFree of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "fat-control.h"

#include "../vfs_server.h"
#include "../byteorder.h"
#include "../block.h"
#include "../div.h"


#include <nxos/utils.h>
#include <nxos/time.h>

static int __fatfs_control_flush_fat_cache(struct fatfs_control_t * ctrl, NX_U32 index)
{
	NX_U32 i, sect_num;
	NX_U64 fat_base, len;

	if(!ctrl->fat_cache_dirty[index])
		return 0;

	for(i = 0; i < ctrl->number_of_fat; i++)
	{
		fat_base = ((NX_U64)ctrl->first_fat_sector + (i * ctrl->sectors_per_fat)) * ctrl->bytes_per_sector;
		sect_num = ctrl->fat_cache_num[index];
		len = NX_IoBlockWrite(ctrl->bdev, &ctrl->fat_cache_buf[index * ctrl->bytes_per_sector], fat_base + sect_num * ctrl->bytes_per_sector,
		        ctrl->bytes_per_sector);
		if(len != ctrl->bytes_per_sector)
			return -1;
	}
	ctrl->fat_cache_dirty[index] = NX_False;

	return 0;
}

static int __fatfs_control_find_fat_cache(struct fatfs_control_t * ctrl, NX_U32 sect_num)
{
	int index;

	for(index = 0; index < FAT_TABLE_CACHE_SIZE; index++)
	{
		if(ctrl->fat_cache_num[index] == sect_num)
			return index;
	}
	return -1;
}

static int __fatfs_control_load_fat_cache(struct fatfs_control_t * ctrl, NX_U32 sect_num)
{
	int rc;
	NX_U32 index;
	NX_U64 fat_base, len;

	if(-1 < __fatfs_control_find_fat_cache(ctrl, sect_num))
		return 0;

	index = ctrl->fat_cache_victim;
	ctrl->fat_cache_victim++;
	if(ctrl->fat_cache_victim == FAT_TABLE_CACHE_SIZE)
		ctrl->fat_cache_victim = 0;

	rc = __fatfs_control_flush_fat_cache(ctrl, index);
	if(rc)
		return rc;

	fat_base = (NX_U64) ctrl->first_fat_sector * ctrl->bytes_per_sector;
	len = NX_IoBlockRead(ctrl->bdev, &ctrl->fat_cache_buf[index * ctrl->bytes_per_sector], fat_base + sect_num * ctrl->bytes_per_sector, ctrl->bytes_per_sector);
	if(len != ctrl->bytes_per_sector)
		return -1;
	ctrl->fat_cache_num[index] = sect_num;

	return 0;
}

static NX_U32 __fatfs_control_read_fat_cache(struct fatfs_control_t * ctrl, NX_U8 * buf, NX_U32 pos)
{
	int rc, index;
	NX_U32 ret, sect_num, sect_off;

	if((ctrl->sectors_per_fat * ctrl->bytes_per_sector) <= pos)
		return 0;

	sect_num = NX_UDiv32(pos, ctrl->bytes_per_sector);
	sect_off = pos - (sect_num * ctrl->bytes_per_sector);

	rc = __fatfs_control_load_fat_cache(ctrl, sect_num);
	if(rc)
		return 0;

	index = __fatfs_control_find_fat_cache(ctrl, sect_num);
	if(index < 0)
		return 0;

	index = (index * ctrl->bytes_per_sector) + sect_off;

	switch(ctrl->type)
	{
	case FAT_TYPE_12:
	case FAT_TYPE_16:
		ret = 2;
		buf[0] = ctrl->fat_cache_buf[index + 0];
		buf[1] = ctrl->fat_cache_buf[index + 1];
		break;
	case FAT_TYPE_32:
		ret = 4;
		buf[0] = ctrl->fat_cache_buf[index + 0];
		buf[1] = ctrl->fat_cache_buf[index + 1];
		buf[2] = ctrl->fat_cache_buf[index + 2];
		buf[3] = ctrl->fat_cache_buf[index + 3];
		break;
	default:
		ret = 0;
		break;
	};

	return ret;
}

static NX_U32 __fatfs_control_write_fat_cache(struct fatfs_control_t * ctrl, NX_U8 * buf, NX_U32 pos)
{
	int rc, index, cache_buf_index;
	NX_U32 ret, sect_num, sect_off;

	if((ctrl->sectors_per_fat * ctrl->bytes_per_sector) <= pos)
		return 0;

	sect_num = NX_UDiv32(pos, ctrl->bytes_per_sector);
	sect_off = pos - (sect_num * ctrl->bytes_per_sector);

	rc = __fatfs_control_load_fat_cache(ctrl, sect_num);
	if(rc)
		return 0;

	index = __fatfs_control_find_fat_cache(ctrl, sect_num);
	if(index < 0)
		return 0;

	cache_buf_index = (index * ctrl->bytes_per_sector) + sect_off;

	switch(ctrl->type)
	{
	case FAT_TYPE_12:
	case FAT_TYPE_16:
		ret = 2;
		ctrl->fat_cache_buf[cache_buf_index + 0] = buf[0];
		ctrl->fat_cache_buf[cache_buf_index + 1] = buf[1];
		break;
	case FAT_TYPE_32:
		ret = 4;
		ctrl->fat_cache_buf[cache_buf_index + 0] = buf[0];
		ctrl->fat_cache_buf[cache_buf_index + 1] = buf[1];
		ctrl->fat_cache_buf[cache_buf_index + 2] = buf[2];
		ctrl->fat_cache_buf[cache_buf_index + 3] = buf[3];
		break;
	default:
		ret = 0;
		break;
	};
	ctrl->fat_cache_dirty[index] = NX_True;

	return ret;
}

static NX_U32 __fatfs_control_first_valid_cluster(struct fatfs_control_t * ctrl)
{
	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		return FAT12_RESERVED1_CLUSTER + 1;
		break;
	case FAT_TYPE_16:
		return FAT16_RESERVED1_CLUSTER + 1;
		break;
	case FAT_TYPE_32:
		return FAT32_RESERVED1_CLUSTER + 1;
		break;
	};
	return 0x0;
}

static NX_U32 __fatfs_control_last_valid_cluster(struct fatfs_control_t * ctrl)
{
	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		return FAT12_RESERVED2_CLUSTER - 1;
		break;
	case FAT_TYPE_16:
		return FAT16_RESERVED2_CLUSTER - 1;
		break;
	case FAT_TYPE_32:
		return FAT32_RESERVED2_CLUSTER - 1;
		break;
	}
	return 0x0;
}

static NX_Bool __fatfs_control_valid_cluster(struct fatfs_control_t * ctrl, NX_U32 cl)
{
	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		if((cl <= FAT12_RESERVED1_CLUSTER) || (FAT12_RESERVED2_CLUSTER <= cl))
			return NX_False;
		break;
	case FAT_TYPE_16:
		if((cl <= FAT16_RESERVED1_CLUSTER) || (FAT16_RESERVED2_CLUSTER <= cl))
			return NX_False;
		break;
	case FAT_TYPE_32:
		if((cl <= FAT32_RESERVED1_CLUSTER) || (FAT32_RESERVED2_CLUSTER <= cl))
			return NX_False;
		break;
	};
	return NX_True;
}

static int __fatfs_control_get_next_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 * next)
{
	NX_U8 fat_entry_b[4] = { 0 };
	NX_U32 fat_entry, fat_off, fat_len, len;

	if(!__fatfs_control_valid_cluster(ctrl, clust))
		return -1;

	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		if(clust % 2)
			fat_off = (clust - 1) * 12 / 8 + 1;
		else
			fat_off = clust * 12 / 8;
		fat_len = 2;
		break;
	case FAT_TYPE_16:
		fat_off = clust * 2;
		fat_len = 2;
		break;
	case FAT_TYPE_32:
		fat_off = clust * 4;
		fat_len = 4;
		break;
	default:
		return -1;
	};

	len = __fatfs_control_read_fat_cache(ctrl, &fat_entry_b[0], fat_off);
	if(len != fat_len)
		return -1;

	if(fat_len == 2)
		fat_entry = ((NX_U32) fat_entry_b[1] << 8) | ((NX_U32) fat_entry_b[0]);
	else
		fat_entry = ((NX_U32) fat_entry_b[3] << 24) | ((NX_U32) fat_entry_b[2] << 16) | ((NX_U32) fat_entry_b[1] << 8) | ((NX_U32) fat_entry_b[0]);

	if(ctrl->type == FAT_TYPE_12)
	{
		if(clust % 2)
			fat_entry >>= 4;
		else
			fat_entry &= 0xFFF;
	}

	if(next)
		*next = fat_entry;

	return 0;
}

static int __fatfs_control_set_next_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 next)
{
	NX_U8 fat_entry_b[4] = { 0 };
	NX_U32 fat_entry, fat_off, fat_len, len;

	if(!__fatfs_control_valid_cluster(ctrl, clust))
		return -1;

	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		if(clust % 2)
			fat_off = (clust - 1) * 12 / 8 + 1;
		else
			fat_off = clust * 12 / 8;
		fat_len = 2;
		len = __fatfs_control_read_fat_cache(ctrl, &fat_entry_b[0], fat_off);
		if(len != fat_len)
			return -1;
		fat_entry = ((NX_U32) fat_entry_b[1] << 8) | fat_entry_b[0];
		if(clust % 2)
		{
			fat_entry &= ~0xFFF0;
			fat_entry |= (next & 0xFFF) << 4;
		}
		else
		{
			fat_entry &= ~0xFFF;
			fat_entry |= (next & 0xFFF);
		}
		fat_entry_b[1] = (fat_entry >> 8) & 0xFF;
		fat_entry_b[0] = (fat_entry) & 0xFF;
		break;
	case FAT_TYPE_16:
		fat_off = clust * 2;
		fat_len = 2;
		fat_entry = next & 0xFFFF;
		fat_entry_b[1] = (fat_entry >> 8) & 0xFF;
		fat_entry_b[0] = (fat_entry) & 0xFF;
		break;
	case FAT_TYPE_32:
		fat_off = clust * 4;
		fat_len = 4;
		fat_entry = next & 0xFFFFFFFF;
		fat_entry_b[3] = (fat_entry >> 24) & 0xFF;
		fat_entry_b[2] = (fat_entry >> 16) & 0xFF;
		fat_entry_b[1] = (fat_entry >> 8) & 0xFF;
		fat_entry_b[0] = (fat_entry) & 0xFF;
		break;
	default:
		return -1;
	};

	len = __fatfs_control_write_fat_cache(ctrl, &fat_entry_b[0], fat_off);
	if(len != fat_len)
		return -1;

	return 0;
}

static int __fatfs_control_set_last_cluster(struct fatfs_control_t * ctrl, NX_U32 clust)
{
	int rc;

	if(!__fatfs_control_valid_cluster(ctrl, clust))
		return -1;

	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		rc = __fatfs_control_set_next_cluster(ctrl, clust, FAT12_LAST_CLUSTER);
		break;
	case FAT_TYPE_16:
		rc = __fatfs_control_set_next_cluster(ctrl, clust, FAT16_LAST_CLUSTER);
		break;
	case FAT_TYPE_32:
		rc = __fatfs_control_set_next_cluster(ctrl, clust, FAT32_LAST_CLUSTER);
		break;
	default:
		rc = -1;
		break;
	};

	return rc;
}

static int __fatfs_control_nth_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 pos, NX_U32 * next)
{
	int rc;
	NX_U32 i;

	if(next)
		*next = clust;

	if(!__fatfs_control_valid_cluster(ctrl, clust))
		return -1;

	for(i = 0; i < pos; i++)
	{
		rc = __fatfs_control_get_next_cluster(ctrl, clust, &clust);
		if(rc)
			return rc;

		if(next)
			*next = clust;

		if(!__fatfs_control_valid_cluster(ctrl, clust))
			return -1;
	}
	return 0;
}

static int __fatfs_control_alloc_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 * newclust)
{
	int rc;
	NX_Bool found;
	NX_U32 current, next, first, last;

	found = NX_False;

	if(__fatfs_control_valid_cluster(ctrl, clust))
	{
		first = clust;
	}
	else
	{
		first = __fatfs_control_first_valid_cluster(ctrl);
	}

	last = __fatfs_control_last_valid_cluster(ctrl);
	for(current = first; current <= last; current++)
	{
		rc = __fatfs_control_get_next_cluster(ctrl, current, &next);
		if(rc)
			return rc;

		if(next == 0x0)
		{
			found = NX_True;
			break;
		}
	}

	if(!found)
		return -1;

	rc = __fatfs_control_set_last_cluster(ctrl, current);
	if(rc)
		return rc;

	if(newclust)
		*newclust = current;

	return 0;
}

static int __fatfs_control_append_free_cluster(struct fatfs_control_t *ctrl, NX_U32 clust, NX_U32 *newclust)
{
	int rc;

	rc = __fatfs_control_alloc_cluster(ctrl, clust, newclust);
	if(rc)
		return rc;

	rc = __fatfs_control_set_next_cluster(ctrl, clust, *newclust);
	if(rc)
		return rc;

	return 0;
}

static int __fatfs_control_truncate_clusters(struct fatfs_control_t *ctrl, NX_U32 clust)
{
	int rc;
	NX_U32 current, next = clust;

	while(__fatfs_control_valid_cluster(ctrl, next))
	{
		current = next;

		rc = __fatfs_control_get_next_cluster(ctrl, current, &next);
		if(rc)
			return rc;

		rc = __fatfs_control_set_next_cluster(ctrl, current, 0x0);
		if(rc)
			return rc;
	}

	return 0;
}

NX_PRIVATE void fattime2time(NX_U32 year, NX_U32 mon, NX_U32 day, NX_U32 hour, NX_U32 min, NX_U32 sec, NX_Time * time)
{
    time->year = 1980 + year - 1900;
    time->month = mon - 1;
    time->day = day;
    time->hour = hour;
    time->minute = min;
    time->second = sec;
}

NX_PRIVATE void time2fattime(NX_Time * time, NX_U32 * year, NX_U32 * mon, NX_U32 * day, NX_U32 * hour, NX_U32 * min, NX_U32 * sec)
{
    *year = time->year + 1900 - 1980;
    *mon = time->month + 1;
    *day = time->day;
    *hour = time->hour;
    *min = time->minute;
    *sec = time->second;
}

NX_TimeStamp fatfs_pack_timestamp(NX_U32 year, NX_U32 mon, NX_U32 day, NX_U32 hour, NX_U32 min, NX_U32 sec)
{
    NX_Time tm;

    fattime2time(year, mon, day, hour, min, sec, &tm);
    return NX_TimeToTimeStamp(&tm);
}

void fatfs_current_timestamp(NX_U32 * year, NX_U32 * mon, NX_U32 * day, NX_U32 * hour, NX_U32 * min, NX_U32 * sec)
{
    NX_Time tm;
    NX_TimeGet(&tm);
    /* 1. change std time to fat time */
    time2fattime(&tm, year, mon, day, hour, min, sec);
}

void fatfs_timestamp(NX_TimeStamp * d, NX_U32 * year, NX_U32 * mon, NX_U32 * day, NX_U32 * hour, NX_U32 * min, NX_U32 * sec)
{
    NX_Time tm;

    NX_TimeStampToTime(*d, &tm);
    time2fattime(&tm, year, mon, day, hour, min, sec);
}

NX_Bool fatfs_control_valid_cluster(struct fatfs_control_t * ctrl, NX_U32 clust)
{
	return __fatfs_control_valid_cluster(ctrl, clust);
}

int fatfs_control_nth_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 pos, NX_U32 * next)
{
	int rc;

	NX_MutexAcquire(ctrl->fat_cache_lock);
	rc = __fatfs_control_nth_cluster(ctrl, clust, pos, next);
	NX_MutexRelease(ctrl->fat_cache_lock);

	return rc;
}

int fatfs_control_set_last_cluster(struct fatfs_control_t * ctrl, NX_U32 clust)
{
	int rc;

	NX_MutexAcquire(ctrl->fat_cache_lock);
	rc = __fatfs_control_set_last_cluster(ctrl, clust);
	NX_MutexRelease(ctrl->fat_cache_lock);

	return rc;
}

int fatfs_control_alloc_first_cluster(struct fatfs_control_t * ctrl, NX_U32 * newclust)
{
	int rc;

	NX_MutexAcquire(ctrl->fat_cache_lock);
	rc = __fatfs_control_alloc_cluster(ctrl, 0, newclust);
	NX_MutexRelease(ctrl->fat_cache_lock);

	return rc;
}

int fatfs_control_append_free_cluster(struct fatfs_control_t * ctrl, NX_U32 clust, NX_U32 * newclust)
{
	int rc;

	NX_MutexAcquire(ctrl->fat_cache_lock);
	rc = __fatfs_control_append_free_cluster(ctrl, clust, newclust);
	NX_MutexRelease(ctrl->fat_cache_lock);

	return rc;
}

int fatfs_control_truncate_clusters(struct fatfs_control_t * ctrl, NX_U32 clust)
{
	int rc;

	NX_MutexAcquire(ctrl->fat_cache_lock);
	rc = __fatfs_control_truncate_clusters(ctrl, clust);
	NX_MutexRelease(ctrl->fat_cache_lock);

	return rc;
}

int fatfs_control_sync(struct fatfs_control_t * ctrl)
{
	int rc, index;

	/* Flush entire FAT sector cache */
	NX_MutexAcquire(ctrl->fat_cache_lock);
	for(index = 0; index < FAT_TABLE_CACHE_SIZE; index++)
	{
		rc = __fatfs_control_flush_fat_cache(ctrl, index);
		if(rc)
		{
			NX_MutexRelease(ctrl->fat_cache_lock);
			return rc;
		}
	}
	NX_MutexRelease(ctrl->fat_cache_lock);

	/* Flush cached data in device request queue */
	NX_IoBlockSync(ctrl->bdev);

	return 0;
}

int fatfs_control_init(struct fatfs_control_t * ctrl,  NX_Solt bdev)
{
	NX_U32 i;
	NX_U64 rlen;
	struct fat_bootsec_t *bsec = &ctrl->bsec;

	/* Read boot sector from block device */
	rlen = NX_IoBlockRead(bdev, (NX_U8 *) bsec,
	FAT_BOOTSECTOR_OFFSET, sizeof(struct fat_bootsec_t));
	if(rlen != sizeof(struct fat_bootsec_t))
		return -1;

	/* Save underlying block device pointer */
	ctrl->bdev = bdev;

	/* Get bytes_per_sector and sector_per_cluster */
	ctrl->bytes_per_sector = NX_Le16ToCpu(bsec->bytes_per_sector);
	ctrl->sectors_per_cluster = bsec->sectors_per_cluster;

	/* Sanity check bytes_per_sector and sector_per_cluster */
	if(!ctrl->bytes_per_sector || !ctrl->sectors_per_cluster)
		return -1;

	/* Frequently required info */
	ctrl->number_of_fat = bsec->number_of_fat;
	ctrl->bytes_per_cluster = ctrl->sectors_per_cluster * ctrl->bytes_per_sector;
	ctrl->total_sectors = NX_Le16ToCpu(bsec->total_sectors_16);
	if(!ctrl->total_sectors)
		ctrl->total_sectors = NX_Le32ToCpu(bsec->total_sectors_32);

	/* Calculate derived info assuming FAT12/FAT16 */
	ctrl->first_fat_sector = NX_Le16ToCpu(bsec->reserved_sector_count);
	ctrl->sectors_per_fat = NX_Le16ToCpu(bsec->sectors_per_fat);
	ctrl->fat_sectors = ctrl->number_of_fat * ctrl->sectors_per_fat;

	ctrl->first_root_sector = ctrl->first_fat_sector + ctrl->fat_sectors;
	ctrl->root_sectors = (NX_Le16ToCpu(bsec->root_entry_count) * 32) + (ctrl->bytes_per_sector - 1);
	ctrl->root_sectors = NX_UDiv32(ctrl->root_sectors, ctrl->bytes_per_sector);
	ctrl->first_root_cluster = 0;

	ctrl->first_data_sector = ctrl->first_root_sector + ctrl->root_sectors;
	ctrl->data_sectors = ctrl->total_sectors - ctrl->first_data_sector;
	ctrl->data_clusters = NX_UDiv32(ctrl->data_sectors, ctrl->sectors_per_cluster);

	/* Determine FAT type */
	if(ctrl->data_clusters < 4085)
		ctrl->type = FAT_TYPE_12;
	else if(ctrl->data_clusters < 65525)
		ctrl->type = FAT_TYPE_16;
	else
		ctrl->type = FAT_TYPE_32;

	/* FAT type sanity check */
	switch(ctrl->type)
	{
	case FAT_TYPE_12:
		if(NX_MemCompareN(bsec->ext.e16.fs_type, "FAT12", 5))
			return -1;
		break;
	case FAT_TYPE_16:
		if(NX_MemCompareN(bsec->ext.e16.fs_type, "FAT16", 5))
			return -1;
		break;
	case FAT_TYPE_32:
		if(NX_MemCompareN(bsec->ext.e32.fs_type, "FAT32", 5))
			return -1;
		break;
	default:
		return -1;
	}

	/* For FAT32, recompute derived info */
	if(ctrl->type == FAT_TYPE_32)
	{
		ctrl->first_fat_sector = NX_Le16ToCpu(bsec->reserved_sector_count);
		ctrl->sectors_per_fat = NX_Le32ToCpu(bsec->ext.e32.sectors_per_fat);
		ctrl->fat_sectors = ctrl->number_of_fat * ctrl->sectors_per_fat;

		ctrl->first_root_sector = 0;
		ctrl->root_sectors = 0;
		ctrl->first_root_cluster = NX_Le32ToCpu(bsec->ext.e32.root_directory_cluster);

		ctrl->first_data_sector = ctrl->first_fat_sector + ctrl->fat_sectors;
		ctrl->data_sectors = ctrl->total_sectors - ctrl->first_data_sector;
		ctrl->data_clusters = NX_UDiv32(ctrl->data_sectors, ctrl->sectors_per_cluster);
	}

	/* Initialize fat cache */
	ctrl->fat_cache_lock = NX_MutexCreate(0);
	ctrl->fat_cache_victim = 0;
	for(i = 0; i < FAT_TABLE_CACHE_SIZE; i++)
	{
		ctrl->fat_cache_dirty[i] = NX_False;
		ctrl->fat_cache_num[i] = i;
	}
	ctrl->fat_cache_buf = NX_MemAlloc(FAT_TABLE_CACHE_SIZE * ctrl->bytes_per_sector);
	if(!ctrl->fat_cache_buf)
		return -1;

	/* Load fat cache */
	rlen = NX_IoBlockRead(ctrl->bdev, ctrl->fat_cache_buf, ctrl->first_fat_sector * ctrl->bytes_per_sector,
	FAT_TABLE_CACHE_SIZE * ctrl->bytes_per_sector);
	if(rlen != (FAT_TABLE_CACHE_SIZE * ctrl->bytes_per_sector))
	{
		NX_MemFree(ctrl->fat_cache_buf);
		return -1;
	}

	return 0;
}

int fatfs_control_exit(struct fatfs_control_t * ctrl)
{
	NX_MemFree(ctrl->fat_cache_buf);
	NX_MutexDestroy(ctrl->fat_cache_lock);
	return 0;
}
